package com.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.github.benmanes.caffeine.cache.Cache;
import com.stock.domain.vo.response.PageResponse;
import com.stock.domain.vo.response.Response;
import com.stock.domain.vo.response.ResponseCode;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.stock.mapper.StockBlockRtInfoMapper;
import com.stock.mapper.StockMarketIndexInfoMapper;
import com.stock.mapper.StockRtInfoMapper;
import com.stock.pojo.domain.*;
import com.stock.pojo.vo.StockInfoConfig;
import com.stock.service.StockService;
import com.stock.utils.DateTimeUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

@Slf4j
@ApiModel
@Service
public class StockServiceImpl implements StockService {

    @ApiModelProperty(hidden = true)
    @Autowired
    private StockInfoConfig stockInfoConfig;

    @ApiModelProperty(hidden = true)
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @ApiModelProperty(hidden = true)
    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;

    @ApiModelProperty(hidden = true)
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Autowired
    private Cache<String,Object> caffeineCache;

    //查询A股大盘实时信息
    @Override
    public Response<List<InnerMarketDomain>> getInnerMarketAll() {
        //TODO 从缓存中加载数据，如果不存在，则走补偿策略获取数据，并存入本地缓存
        Response<List<InnerMarketDomain>> data = (Response<List<InnerMarketDomain>>)
                caffeineCache.get("innerMarketInfosKey", (key) -> {
            //TODO 补偿策略
            log.info("补偿策略执行");
            //1.获取A股集合
            List<String> inners = stockInfoConfig.getInner();
            //2.获取最近股票交易时间
            Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
            lastDate = DateTime.parse("2024-11-8 14:51:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

            //3.将获取的时间传入接口
            List<InnerMarketDomain> innerMarketDomains = stockMarketIndexInfoMapper.getInnerMarketAll(inners, lastDate);
            //4.返回数据
            return Response.ok(innerMarketDomains);
        });
        return data;
    }

    //获取沪深两市板块最新数据，以交易总金额降序查询，取前10条数据
    @Override
    public Response<List<StockBlockDomain>> getSectorLimit10() {
        //1.获取股票最新交易时间点
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //TODO mock数据,后续删除
        lastDate = DateTime.parse("2024-11-8 16:35:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.调用mapper层接口，获取数据
        List<StockBlockDomain> infos = stockBlockRtInfoMapper.getSectorLimit10(lastDate);
        //3.返回数据
        //无数据情况
        if (infos == null) {
            return Response.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        //正常返回
        return Response.ok(infos);
    }

    //分页查询股票最新数据，并按照涨幅排序查询
    @Override
    public Response<PageResponse> getStockPageInfo(Integer page, Integer pageSize) {
        //1.获取股票最新交易时间点
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //TODO mock数据,后续删除
        lastDate = DateTime.parse("2021-12-20 10:26:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        //2.设置分页参数
        PageHelper.startPage(page, pageSize);
        //3.调用mapper层接口，获取数据
        List<StockUpdownDomain> infos = stockMarketIndexInfoMapper.getStockPageInfo(lastDate);
        if (CollectionUtils.isEmpty(infos)) {
            return Response.error(ResponseCode.NO_RESPONSE_DATA);
        }
        //4.组装返回数据对象
        //将从数据库查询到的分页数据 infos 封装成 PageInfo 对象
        PageInfo<StockUpdownDomain> pageInfo = new PageInfo<>(infos);
        //将 PageInfo 对象封装成 PageResponse 对象作为响应数据
        PageResponse<StockUpdownDomain> pageRes = new PageResponse<>(pageInfo);
        return Response.ok(pageRes);
    }

    //统计沪深两市个股最新交易数据，并按涨幅降序排序查询前4条数据
    @Override
    public Response<List<StockUpdownDomain>> getStockIncrease() {
        //1.获取股票最新交易时间点
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //TODO mock数据,后续删除
        lastDate = DateTime.parse("2021-12-20 10:26:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.调用mapper层接口，获取数据
        List<StockUpdownDomain> infos = stockMarketIndexInfoMapper.getStockIncrease(lastDate);
        if (CollectionUtils.isEmpty(infos)) {
            return Response.error(ResponseCode.NO_RESPONSE_DATA);
        }
        //3.返回数据
        return Response.ok(infos);
    }

    //统计最新涨停跌停数据
    @Override
    public Response<Map<String, List>> getStockUpDownCount() {
        //1.获取最新的交易时间范围 openTime  curTime
        //1.1 获取最新股票交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2021-12-24 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = curDateTime.toDate();
        //1.2 获取最新交易时间对应的开盘时间
        Date openDate = DateTimeUtil.getOpenDate(curDateTime).toDate();

        //2.查询涨停数据
        //约定mapper中flag入参： 1: 涨停数据 0：跌停
        List<Map> upList = stockRtInfoMapper.getStockUpdownCount(openDate, endDate, 1);
        //3.查询跌停数据
        List<Map> downList = stockRtInfoMapper.getStockUpdownCount(openDate, endDate, 0);
        //4.组装数据
        HashMap<String, List> mapInfo = new HashMap<>();
        mapInfo.put("upList", upList);
        mapInfo.put("downList", downList);
        //返回结果
        return Response.ok(mapInfo);
    }

    //将指定页的股票数据导出到excel表下
    @Override
    public void stockExport(Integer page, Integer pageSize, HttpServletResponse response) {
        //1.获取分页数据
        Response<PageResponse> pageInfo = this.getStockPageInfo(page, pageSize);
        //获取每一行的数据
        List<StockUpdownDomain> rows = pageInfo.getData().getRows();

        //2.将数据导出到execl中
        //2.1表示响应的数据是一个Excel，浏览器会通知用户下载文件
        response.setContentType("application/vnd.ms-excel");
        //2.2设置响应编码
        response.setCharacterEncoding("utf-8");
        try {
            //2.3设置文件名
            String fileName = URLEncoder.encode("股票信息表", "UTF-8");
            //2.4告诉浏览器将响应内容作为一个名为 fileName.xlsx 的文件下载到用户的设备上
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            //2.5 将 rows 列表中的数据写入到一个名为 "股票信息表" 的 Excel 工作表中，
            // 并通过 HTTP 响应流返回给客户端，客户端会下载这个 Excel 文件
            EasyExcel
                    .write(response.getOutputStream(), StockUpdownDomain.class)
                    .sheet("股票信息表")
                    .doWrite(rows);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("当前导出数据异常，当前页：{},每页大小：{},异常信息：{}", page, pageSize, e.getMessage());
        }
    }

    //统计国内A股大盘T日和T-1日成交量对比功能（成交量为沪市和深市成交量之和）
    @Override
    public Response<Map<String, List>> getTradeAmtByInner() {
        //1.获取最新的交易时间范围 openTime  curTime
        //1.1 获取最新股票交易时间点
        DateTime tCurDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        tCurDateTime = DateTime.parse("2021-12-29 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date tEndDate = tCurDateTime.toDate();
        //1.2 获取最新交易时间对应的开盘时间
        Date tOpenDate = DateTimeUtil.getOpenDate(tCurDateTime).toDate();
        //2.获取T-1日交易时间范围
        DateTime preEndDataTime = DateTimeUtil.getPreviousTradingDay(tCurDateTime);
        Date preEndDate = preEndDataTime.toDate();
        DateTime preOpenDateTime = DateTimeUtil.getOpenDate(preEndDataTime);
        Date preOpenDate = preOpenDateTime.toDate();

        //3.1 查询T日国内大盘数据
        //TODO 参数1：开盘时间  参数2：当前时间  参数3：获取国内A股交易所
        List<Map> amtList = stockMarketIndexInfoMapper.getStockTradeVol(tOpenDate, tEndDate, stockInfoConfig.getInner());
        //3.2 查询T-1日国内大盘数据
        List<Map> yesAmtList = stockMarketIndexInfoMapper.getStockTradeVol(preOpenDate, preEndDate, stockInfoConfig.getInner());

        //4.组装数据
        HashMap<String, List> mapInfo = new HashMap<>();
        mapInfo.put("amtList", amtList);
        mapInfo.put("yesAmtList", yesAmtList);
        //5.返回结果
        return Response.ok(mapInfo);
    }

    //统计当前时间下（精确到分钟），A股在各个涨跌区间股票的数量
    @Override
    public Response<Map> getStockUpDown() {
        //1.获取当前时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2021-12-20 10:38:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();

        //2.查询当前时间下，各个涨跌区间股票的数量
        List<Map> infos = stockRtInfoMapper.getStockUpDown(curDate);

        //2.1 获取有序的标题集合
        List<String> orderSections = stockInfoConfig.getUpDownRange();
        //思路：利用List集合的属性，然后顺序编译，找出每个标题对应的map，然后维护到一个新的List集合下即可
        List<Map> orderMaps = new ArrayList<>();
        for (String title : orderSections) {
            Map map = null;
            for (Map m : infos) {
                if (m.get("title").equals(title)) {
                    map = m;
                    break;
                }
            }
            if (map == null) {
                map = new HashMap();
                map.put("title", title);
                map.put("count", 0);
            }
            orderMaps.add(map);
        }

        //3.组装数据
        HashMap<String, Object> mapInfo = new HashMap<>();
        //3.1获取指定格式的日期字符串
        String curDateStr = new DateTime(curDate).toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        mapInfo.put("time", curDateStr);
        mapInfo.put("infos", orderMaps);

        //4.返回结果
        return Response.ok(mapInfo);
    }

    //查询单个个股的分时行情数据，也就是统计指定股票T日每分钟的交易数据
    @Override
    public Response<List<Stock4MinuteDomain>> stockScreenTimeSharing(String stockCode) {
        //1.获取当前时间
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        endDateTime = DateTime.parse("2021-12-30 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = endDateTime.toDate();
        Date openDate = DateTimeUtil.getOpenDate(endDateTime).toDate();

        //2.获取指定股票的分时数据
        List<Stock4MinuteDomain> data = stockRtInfoMapper.getStockInfoByCodeAndDate(stockCode, openDate, endDate);

        //3.返回结果
        return Response.ok(data);
    }

    //单个个股日K 数据查询 ，可以根据时间区间查询数日的K线数据
    @Override
    public Response<List<Stock4EvrDayDomain>> stockCreenDkLine(String stockCode) {
        //1.获取查询的日期范围
        //1.1 获取截止时间
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endTime = endDateTime.toDate();
        //TODO 假数据
        endTime = DateTime.parse("2021-12-30 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //1.2 获取开始时间
        DateTime startDateTime = endDateTime.minusDays(10);
        Date startTime = startDateTime.toDate();
        //TODO 假数据
        startTime = DateTime.parse("2021-12-20 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        //2.调用mapper接口获取查询的集合信息-方案1
        List<Stock4EvrDayDomain> data = stockRtInfoMapper.getStockInfo4EvrDay(stockCode, startTime, endTime);

        //3.返回结果
        return Response.ok(data);

    }
}
